package com.junlinpro.tcp;

import java.net.InetSocketAddress;
import java.util.Date;
import java.util.List;

import com.junlinpro.OpusSdk;
import com.junlinpro.config.ApplicationContextProvider;
import com.junlinpro.entity.MessageEntity;
import com.junlinpro.entity.RegisterEntity;
import com.junlinpro.service.RealTimeAudioService;
import com.junlinpro.utils.BufferMemory4G;
import com.junlinpro.utils.ByteUtil;
import com.junlinpro.utils.CardStateUtil;
import com.junlinpro.utils.Constant;
import com.junlinpro.utils.CrcUtils;
import com.junlinpro.utils.DateUtil;
import com.junlinpro.utils.MessageUtil;
import com.junlinpro.utils.OpusSdkProvider;
import com.junlinpro.utils.PcmInByteData;
import com.junlinpro.utils.SocketChannelMap;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class TcpChannelHandler extends ChannelHandlerAdapter {
	
    // 采集卡状态管理类（储存到redis中的）
    private CardStateUtil cardStateUtil;
    private PcmInByteData pcmInByteData;
    private BufferMemory4G bufferMemory4G;
    private RealTimeAudioService realTimeAudioServices;    

    public TcpChannelHandler() {
        this.cardStateUtil = ApplicationContextProvider.getBean(CardStateUtil.class);
        this.pcmInByteData = ApplicationContextProvider.getBean(PcmInByteData.class);
        this.bufferMemory4G = ApplicationContextProvider.getBean(BufferMemory4G.class);        
        this.realTimeAudioServices = ApplicationContextProvider.getBean(RealTimeAudioService.class);       
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
//        log.info("ctx.toString()="+ctx.toString());
//        log.info("currentThread id ="+Thread.currentThread().getId());
        try {
            // 将数据转换为Byte数组
            ByteBuf myBuf = (ByteBuf) msg;
            byte[] msgByteArray = new byte[myBuf.readableBytes()];
            myBuf.readBytes(msgByteArray);
            ReferenceCountUtil.release(myBuf);
                
                InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
                String clientIP = insocket.getAddress().getHostAddress();
                // 将数据放入缓存中
                bufferMemory4G.tcpAdd(clientIP, msgByteArray);
                // 从缓存中读取
                List<byte[]> memoryDataList = bufferMemory4G.getTcpData(clientIP);
                
                if (memoryDataList != null && !memoryDataList.isEmpty() ) {
                    for (int j = 0; j < memoryDataList.size(); j++) {
                        // 声明一个ByteBuf
                        ByteBuf buf = ByteBufAllocator.DEFAULT.buffer();
                        buf.writeBytes(memoryDataList.get(j));
                        int msgLen = buf.readableBytes() - 4;
                        System.out.println("msgLen="+msgLen);
                        byte[] crcData = new byte[msgLen];
                        buf.getBytes(0, crcData);
                        
                        //System.out.println("crcData="+CrcUtils.printHexBinary(crcData));
                        
                        byte[] crcValue = new byte[2];
                        buf.getBytes(msgLen, crcValue, 0, 2);
                        
                        byte[] crcResult = CrcUtils.getCRCValue(crcData);
                        
                       
                        //System.out.println(CrcUtils.byteToHex(crcResult[0])+" "+CrcUtils.byteToHex(crcResult[1]));
                        // crc校验
                        if (crcValue[0] == crcResult[0] && crcValue[1] == crcResult[1]) {
                        	log.info("--- TCP crc校验通过 ---");
                        	System.out.println(CrcUtils.byteToHex(crcValue[0])+" "+CrcUtils.byteToHex(crcValue[1]));
                        	
                        	MessageEntity messageEntity = MessageUtil.newBufToMessage(buf);
                          
                            // 获取帧序号
                            int frameId = messageEntity.getFrameId();

                            if (messageEntity.getFrameHead()[0] == 74 && messageEntity.getFrameHead()[1] == 76) {
                                // 获得硬件上报的采集卡编号
                                int cardId = messageEntity.getCardId();
                                // 获得通道号
                                byte channelId = messageEntity.getChannelId();
                                if (frameId == 0 || frameId % 375 == 0) {
                                    SocketChannelMap.heartbeatChannelMapAdd(String.valueOf(cardId), ctx.channel());
                                    //xiazq 记录采集卡的哪些状态?
                                    //cardStateUtil.cardStateAdd(String.valueOf(cardId));
                                }
                                
                                if (messageEntity.getCmd() == Constant.CMD_CARD_REGISTER) {
                                	
                                    RegisterEntity registerEntity = new RegisterEntity();
                                    registerEntity.setCardId(messageEntity.getCardId());
                                    registerEntity.setChannelId(messageEntity.getChannelId());
                                    registerEntity.setVersionNum(messageEntity.getVersionNum());
                                    if (messageEntity.getCardType() == 0) {
                                        registerEntity.setCardType(1);
                                    } else {
                                        registerEntity.setCardType(2);
                                    }
                                    realTimeAudioServices.register(ctx, registerEntity, 1);
                                    
                                } else if (messageEntity.getCmd() == Constant.CMD_HEART_BEAT) { // 心跳逻辑
                                	
                                    log.info("-- 收到心跳 --"+messageEntity.getCardId()+messageEntity.getChannelId());
                                    SocketChannelMap.add(String.valueOf(cardId), ctx.channel());
                                    
                                } else if (messageEntity.getCmd() == Constant.CMD_START_REALTIME) { // 实时上报的音频数据
                                	
                                	 if (frameId == 0 || frameId % 375 == 0) {
                                         //xiazq 记录采集卡在实时监听
                                         cardStateUtil.cardStateAdd(String.valueOf(cardId));
                                     }
                                	
                                    synchronized (this) {
                                        // 解析协议
                                        // 获得音频数据
                                        byte[] dataArr = messageEntity.getData();
                                        // 为了保护协议格式统一在数据都是有12个预置字节的，由于在此处无用所以要删除掉
                                        byte[] pcmData = new byte[dataArr.length - 12];
                                        int g = 0;
                                        for (int i = 12; i < dataArr.length; i++) {
                                            pcmData[g] = dataArr[i];
                                            g++;
                                        }
                                        ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer();
                                        byteBuf.writeBytes(pcmData);
                                        
                                        if (channelId == 80) {
                                        	// 如果通道号等于2说明是双通道 ，实时不存在双通道 所以不处理
                                        } else {
                                            // 单通道音频处理逻辑
                                            // 单通道上报的通道号就是监听选择建通的通道号，所以直接用上报上来的通道号做存储就可以
                                        	int count = (messageEntity.getFrameId() /Constant.REALTIME_FRAME_SUM)+1;
                                        	String pcmKey = messageEntity.getCardId() + "_" + messageEntity.getChannelId()+count;

                                        	ByteBuf finalByteBuf = Unpooled.buffer();
                                            for (int i = 0; i < 2; i++) {
                                                byteBuf.skipBytes(4);
                                                byte[] byteLen = new byte[4];
                                                byteBuf.readBytes(byteLen);
                                                int len = ByteUtil.byteArrayToInt(byteLen);
                                                byte[] firstFrame = new byte[len];
                                                byteBuf.readBytes(firstFrame);
                                                
                                                //解码    	
                                        		short[] pcmShort = new short[Constant.OPUS_DECODER_SHORT_BUFFER_SIZE];
                                        		OpusSdk opusSdk = OpusSdkProvider.getOpusSdk();
                                        		opusSdk.opusDecoder(OpusSdkProvider.getOpusId(), firstFrame, pcmShort);                                        		
                                        		byte[] pcmBuffer = ByteUtil.toByteArray(pcmShort);
                                                
                                        		// 使用采集卡编号和通道号的组合当作key                                        		
                                                pcmInByteData.put(pcmKey, pcmBuffer);                   
                                                
                                        		finalByteBuf.writeBytes(pcmBuffer);
                                        		
                                            }
                                            //实时监听每一帧都存入kafka，并发送给前端汤20211009
                                            int finalLen =  finalByteBuf.readableBytes();
                                            byte[] finalData = new byte[finalLen];
                                            finalByteBuf.readBytes(finalData);
                                            ReferenceCountUtil.release(finalByteBuf);
                                            //不需要同步？
                                            messageEntity.setData(null);
                                            realTimeAudioServices.sendSoundToUDP(messageEntity,finalData);
                                            
                                        }
                                        ReferenceCountUtil.release(byteBuf);
                                    }
                                }
                                else if (messageEntity.getCmd() == Constant.CMD_CARD_REGISTER_RTN) { // 注册成功嵌入式给的回复
                                    SocketChannelMap.add(String.valueOf(cardId), ctx.channel());// 根据编号储存Socket链接对象                                
                                    RegisterEntity registerEntity = new RegisterEntity();
                                    registerEntity.setCardId(messageEntity.getCardId());
                                    registerEntity.setChannelId(messageEntity.getChannelId());
                                    registerEntity.setVersionNum(messageEntity.getVersionNum());
                                    // 判断采集卡类型
                                    if (messageEntity.getCardType() == 0) {
                                        registerEntity.setCardType(1);
                                    } else {
                                        registerEntity.setCardType(2);
                                    }
                                    realTimeAudioServices.register(ctx, registerEntity, 2);
                                }
                                else if (messageEntity.getCmd() == Constant.TIME_INTERVAL_RESPONSE) { //收到更改嵌入式更改时间间隔的回复
                                    log.info("--- 收到时间间隔回复 ---");
                                    byte[] dataArr = messageEntity.getData();
                                    // 为了保护协议格式统一在数据都是有12个预置字节的，由于在此处无用所以要删除掉
                                    byte[] timeArr = new byte[dataArr.length - 12];
                                    int g = 0;
                                    for (int i = 12; i < dataArr.length; i++) {
                                        timeArr[g] = dataArr[i];
                                        g++;
                                    }
                                    // 将数据换算成秒数
                                    int timeData = ByteUtil.byteArrayToInt(timeArr) / 1000;
                                    log.info("--- 更新成功 ---");
                                }
                                else if (messageEntity.getCmd() == Constant.CMD_CONNECT) { // 收到这个命令立刻给嵌入式回复，嵌入式用这个判断链路打通
                                    log.info("--- 链路打通命令 ----  cardId="+cardId+messageEntity.getChannelId());
                                    // 根据编号储存Socket链接对象
                                    SocketChannelMap.add(String.valueOf(cardId), ctx.channel());
                                    // 拼接回复数据
                                    MessageEntity rtnMsg = new MessageEntity();
                                    rtnMsg.setCardId(messageEntity.getCardId());
                                    rtnMsg.setChannelId(messageEntity.getChannelId());
                                    byte cmd = 0x12;
                                    rtnMsg.setVersionNum(messageEntity.getVersionNum());
                                    rtnMsg.setCmd(cmd);
                                    // 帧序号
                                    rtnMsg.setFrameId(messageEntity.getFrameId());
                                    // 数据总长度
                                    rtnMsg.setAllLen(23);
                                    // 类型
                                    rtnMsg.setCardType(messageEntity.getCardType());
                                    // 转换成时间戳
                                    int utcTimeStamp = DateUtil.transForMilliSecond(new Date());
                                    rtnMsg.setOccurTime(utcTimeStamp);
                                    byte[] rtnByteMsg = MessageUtil.rtnMessageTobyte(rtnMsg);
                                    // 回复链路包
                                    ctx.writeAndFlush(Unpooled.copiedBuffer(rtnByteMsg));
                                    log.info("--- 链路打通回复指令 ---");
                                    
                                }
                                else if (messageEntity.getCmd() == Constant.CMD_START_REALTIME_RTN) {
                                    SocketChannelMap.taskMapPut(SocketChannelMap.startTaskKey + cardId, true);
                                    log.info("--- 收到监听开始回复 ---");
                                }
                                else if (messageEntity.getCmd() == Constant.CMD_STOP_REALTIME_RTN) {
                                    SocketChannelMap.taskMapPut(SocketChannelMap.stopTaskKey + cardId, true);
                                    
                                    cardStateUtil.cardStateRemove(String.valueOf(cardId));
                                    
                                    log.info("--- 收到监听结束回复 ---");
                                    log.info("GET key-->> "+messageEntity.getCardId() + "_" + messageEntity.getChannelId());
                                    realTimeAudioServices.saveRealTimeData(messageEntity);
                                }
                            } else {
                                log.info("--- 数据头不正确 ---");
                            }
                        } else {    	
                        	log.info("--- TCP crc校验未通过 ---");
                        }
                        ReferenceCountUtil.release(buf);
                    }
                }
        } catch (Exception e) {
        	log.error(e.getMessage());
        	e.printStackTrace();
        } finally {
            //ctx.close();
        }
    }  
    
    /**
     * 
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    	log.error("error [TcpChannelHandler] exceptionCaught: {}", cause.getMessage());
    	ctx.close();
    }

}
